#pragma once
#include <iostream>
#include <string>
#include <functional>

#include <sys/epoll.h>
#include "sock.hpp"
#include "err.hpp"
#include "log.hpp"

namespace YZC
{
    static const uint16_t defaultport = 8000;
    static const int defaultfd = -1;
    static const int size = 128;
    static const int defalultnum = 64; // 处理事件的大小
    using func_t = std::function<std::string(const std::string &)>;

    class EpollServer
    {
    public:
        EpollServer(func_t func, int port = defaultport)
            : _port(port), _epfd(-1), _listensock(-1), _callback(func), _revs(nullptr), _num(defalultnum) {}
        ~EpollServer()
        {
            if (_listensock != defaultfd)
                close(_listensock);
            _listensock = defaultfd;

            if (_epfd > 0)
                close(_epfd);
            _epfd = 0;

            if (_revs)
                delete[] _revs;
            _revs = nullptr;
        }

        void init()
        {
            // 创建套接字
            _listensock = Sock::creatSock();
            // 绑定端口
            Sock::Bind(_listensock, _port);
            // 监听
            Sock::Listen(_listensock);

            // 创建epoll模型
            _epfd = epoll_create(size);
            if (_epfd < 0)
            {
                LogMessage(FATAL, "create epfd error!");
                exit(EPOLL_CREATE_ERR);
            }
            LogMessage(NORMAL, "create epfd success!");

            // 注册linstensockfd到epoll模型中
            struct epoll_event ev;
            ev.events = EPOLLIN;
            ev.data.fd = _listensock;
            epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock, &ev);

            // 申请就绪事件的空间
            _revs = new struct epoll_event[_num];
            LogMessage(NORMAL, "server init success");
        }

        void run()
        {
            int timeout = 1000;
            for (;;)
            {
                int n = epoll_wait(_epfd, _revs, _num, timeout);
                switch (n)
                {
                case 0:
                    LogMessage(NORMAL, "timeout ...");
                    break;
                case -1:
                    LogMessage(ERROR, "epoll error errno:%d errstr:%s", errno, strerror(errno));
                    exit(EPOLL_ERR);
                default:
                    // 监听事件就绪了！即表示有新连接来了！
                    LogMessage(NORMAL, "epoll tell me a event ready!, I should handler!");
                    HandlerReadEvent(_num);
                    break;
                }
            }
        }

    private:
        void HandlerReadEvent(int readyNum)
        {
            LogMessage(DEBUG, "HandlerEvent in");

            // 根据epoll返回的num遍历所有就绪的事件
            for (int i = 0; i < readyNum; i++)
            {
                uint32_t event = _revs[i].events;
                int sock = _revs[i].data.fd;

                if (sock == _listensock && (event & EPOLLIN))
                {
                    // listen套接字的读事件就绪，获取新连接
                    std::string clientip;
                    uint16_t clientport;
                    int fd = Sock::Accept(_listensock, &clientip, &clientport);
                    if (fd < 0)
                    {
                        LogMessage(WARNING, "accept error");
                        continue; // 终止这一次循环，处理后续就绪的数字
                    }

                    // 获取新的fd成功，需要将新的fd传入epoll模型中。让OS帮助我们处理该fd的某一个事件是否就绪
                    struct epoll_event ev;
                    ev.events = EPOLLIN;
                    ev.data.fd = fd;
                    epoll_ctl(_epfd, EPOLL_CTL_ADD, fd, &ev);
                    LogMessage(NORMAL, "accept %d success", sock);
                }
                else if (event & EPOLLIN)
                {
                    // 普通读事件就绪
                    // 这里的读取是有问题的，正确读取应该读取一个完整的报文
                    char buffer[1024];
                    int n = recv(sock, buffer, sizeof(buffer), 0);
                    if (n > 0)
                    {
                        buffer[n] = 0;
                        LogMessage(DEBUG, "client# %s", buffer);

                        // 回显读取到的数据, 这个发送数据是有问题的！
                        std::string response = _callback(buffer);
                        send(sock, response.c_str(), response.size(), 0);
                    }
                    else if (n == 0)
                    {
                        // 客户端发送数据完毕，需要关闭套接字并且将其从epoll模型中去除
                        // 建议先移除epoll再去关闭文件描述符    因为移除的时候要保证该套接字是合法的
                        epoll_ctl(_epfd, EPOLL_CTL_DEL, sock, nullptr);
                        close(sock);
                        sock = defaultfd; // 防止后续误操作
                        LogMessage(NORMAL, "数据已经读取完毕! client quit");
                    }
                    else
                    {
                        epoll_ctl(_epfd, EPOLL_CTL_DEL, sock, nullptr);
                        close(sock);
                        sock = defaultfd;
                        LogMessage(ERROR, "epoll error errno:%d errstr:%s", errno, strerror(errno));
                    }
                }
                else if (event & EPOLLOUT)
                {
                    ;
                }
                else
                    ;
            }
            LogMessage(DEBUG, "HandlerEvent out");
        }

    private:
        uint16_t _port;
        int _epfd;                 // epoll模型的文件描述符fd
        int _listensock;           // 监听套接字fd
        func_t _callback;          // 处理接收数据的回调函数
        struct epoll_event *_revs; // epoll返回的需要处理的事件的指针
        int _num;                  // 需要处理事件的数量
    };
}